
#include "ShowImage.h"
#include <string>

bool ShowPhoto(const char *filename)
{
	bool flag = true;
	// call the relative function, judge by its file type
	if(strstr(filename,".bmp"))
	{
		DisplayBMP(filename);
	}
	else if(strstr(filename,".jpg"))
	{
		DisplayJPG(filename);
	}
	else
	{
		printf("showPhoto() show %s error\n",filename);
		flag = false;
	}

	return flag;
}

// 遍历 Dirname里面的bmp和jpg图片，循环播放一次，间歇为internel
void ShowAllPhotoInDir(const char *Dirname, int interval, int nLoopCnt)
{
    if (nLoopCnt < -1)
    {
        printf("ShowAllPhotoInDir() nLoopCnt error\n");
        return;
    }

    DIR *dirFp = opendir(Dirname);
    if (dirFp == NULL)
    {
        printf("showAllPhotoInDir() opendir error\n");
        return;
    }

    int loopCount = nLoopCnt;
    while (true)
    {
        printf("----------------- ShowAllPhotoInDir() , loopCount = %d\n", loopCount);
        struct dirent *ptr;
        // 重置目录指针到开头
        rewinddir(dirFp);

        while ((ptr = readdir(dirFp)) != NULL)
        {
            // 过滤掉 . 和 .. 以及子文件夹
            if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0 || ptr->d_type == DT_DIR)
            {
                continue;
            }

            std::string filename = std::string(Dirname) + "/" + std::string(ptr->d_name);
            if (ShowPhoto(filename.c_str()) == true)
            {
                printf("Showing Picture %s\n", ptr->d_name);
                // 延时
                sleep(interval);
            }
        }

        // 如果是-1，无限循环
        if (loopCount == -1)
        {
            continue;
        }
        
        loopCount--;
        if (loopCount == 0)
        {
            break;
        }
        
    }

    closedir(dirFp);
}


//=======================================
//+传入图片文件名还有大小，得到图片文件中存储的JPG数据的信息
char * Readjpg(char const *filename, int size) 
{
    // 只读打开这个文件
    int fd_photo = open(filename, O_RDONLY);
    if (fd_photo == -1) 
	{
        perror("Readjpg() Open file failed");
        return nullptr;
    }

    // 用malloc因为其他函数也要使用，要长期存在
    // 而且我们可以控制它的free，所以不用static
    char *jpg_buffer = static_cast<char*>(calloc(1, size));
    if (jpg_buffer == nullptr) 
	{
        perror("memory allocation failed");
        close(fd_photo);
        return nullptr;
    }

    ssize_t total_read = 0;
    while (total_read < size) 
	{
        ssize_t n = read(fd_photo, jpg_buffer + total_read, size - total_read);
        if (n == -1) 
		{
            perror("read file failed");
            free(jpg_buffer);
            close(fd_photo);
            return nullptr;
        }
        total_read += n;
    }

    close(fd_photo);
    // 把这篇存储信息的空间返回；
    return jpg_buffer;
}


void DisplayJPG(const char *filename) 
{
    // 获取图片的文件信息，主要是大小
    struct stat info;
    if (stat(filename, &info) == -1) 
	{
        perror("Failed to get file information");
        return;
    }

    // 读取 JPG 数据
    char *jpg_buffer = Readjpg(filename, info.st_size);
    if (jpg_buffer == nullptr) 
	{
        return;
    }

    // 声明解压缩结构体和错误管理结构体
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 设置错误处理函数，并创建解压缩对象
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 配置解压缩结构体，配置 cinfo 从 jpg_buffer 中读取指定大小的数据进行解压缩。
    jpeg_mem_src(&cinfo, (const unsigned char*)jpg_buffer, info.st_size);

    // 读取 JPEG 文件头
    if (jpeg_read_header(&cinfo, TRUE) != 1) 
	{
        fprintf(stderr, "[%d]: jpeg_read_header failed: %s\n", __LINE__, strerror(errno));
        jpeg_destroy_decompress(&cinfo);
        free(jpg_buffer);
        return;
    }

    // 开始解压
    jpeg_start_decompress(&cinfo);

    // 获取解压后图片信息，保存图片的宽、高、深度信息
    struct imginfo imageinfo;
    imageinfo.width = cinfo.output_width;
    imageinfo.height = cinfo.output_height;
    imageinfo.pixel_size = cinfo.output_components;

    int row_stride = imageinfo.width * imageinfo.pixel_size;

    // 分配内存用于存放解压后的图像数据
    unsigned long rgb_size = imageinfo.width * imageinfo.height * imageinfo.pixel_size;
    unsigned char *rgb_buffer = (unsigned char *)calloc(1, rgb_size);
    if (rgb_buffer == nullptr) 
	{
        perror("Failed to allocate memory for RGB buffer");
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
        free(jpg_buffer);
        return;
    }

    // 循环解压图片的每一行
    while (cinfo.output_scanline < cinfo.output_height) 
	{
        unsigned char *buffer_array[1];
        buffer_array[0] = rgb_buffer + cinfo.output_scanline * row_stride;
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }

    // 完成解压，释放 JPEG 相关资源
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    free(jpg_buffer);

    // 打开 LCD 文件
    int lcd = open("/dev/fb0", O_RDWR);
    if (lcd == -1) 
	{
        perror("Failed to open LCD device");
        free(rgb_buffer);
        return;
    }

    // 获取 LCD 屏幕参数
    struct fb_var_screeninfo vinfo;
    if (ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo) == -1) 
	{
        perror("Failed to get LCD screen information");
        close(lcd);
        free(rgb_buffer);
        return;
    }

    // 简化变量名
    int lcd_w = vinfo.xres;
    int lcd_h = vinfo.yres;
    int lcd_bpp = vinfo.bits_per_pixel;

    int w = imageinfo.width;
    int h = imageinfo.height;

    // 映射 LCD 内存
    int *p = (int *)mmap(NULL, lcd_w * lcd_h * lcd_bpp / 8, PROT_READ | PROT_WRITE, MAP_SHARED, lcd, 0);
    if (p == MAP_FAILED) {
        perror("Failed to mmap LCD memory");
        close(lcd);
        free(rgb_buffer);
        return;
    }

    // 显示图片
    int copy_width = (lcd_w < w) ? lcd_w : w;
    int copy_height = (lcd_h < h) ? lcd_h : h;
    for (int j = 0; j < copy_height; j++) 
	{
        for (int i = 0; i < copy_width; i++) 
		{
            unsigned char red 	= rgb_buffer[3 * (i + w * j) + 0];
            unsigned char green = rgb_buffer[3 * (i + w * j) + 1];
            unsigned char blue 	= rgb_buffer[3 * (i + w * j) + 2];

            unsigned int tmp = (0xFF << 24) | (red << 16) | (green << 8) | blue;
            p[i + lcd_w * j] = tmp;
        }
    }

    // 释放资源
    munmap(p, lcd_w * lcd_h * lcd_bpp / 8);
    free(rgb_buffer);
    close(lcd);
}




// 函数名和参数保持不变
void DisplayBMP(const char *filename)
{
    // 1, 打开图片
    int fd = open(filename, O_RDONLY);
    if (fd == -1)
    {
        printf("读取文件失败\n");
        exit(0);
    }

    // 2, 读取头部信息，从图片文件中，头部信息包含图片大小，深度
    // 修改结构体变量名，使其更具描述性
    struct BMPFileHeader bmpFileHeader;
    struct BMPInfoHeader bmpInfoHeader;

    bzero(&bmpFileHeader, sizeof(bmpFileHeader));
    bzero(&bmpInfoHeader, sizeof(bmpInfoHeader));

    if (read(fd, &bmpFileHeader, sizeof(bmpFileHeader)) != sizeof(bmpFileHeader) || read(fd, &bmpInfoHeader, sizeof(bmpInfoHeader)) != sizeof(bmpInfoHeader))
    {
        perror("Failed to read BMP headers");
        close(fd);
        return;
    }

    // 3, 准备LCD设备
    int lcd = open("/dev/fb0", O_RDWR);
    if (lcd == -1)
    {
        perror("Failed to open LCD device");
        close(fd);
        return;
    }

    //3.1获取lcd屏幕的参数大小，其实就是800*480
    struct fb_var_screeninfo vinfo;
    bzero(&vinfo, sizeof(vinfo));

    ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo);

    //申请映射内存         //宽        高        深度（位）/8 = 深度（字节）
    size_t lcd_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
	// lcd_size = 1536000, vinfo.xres = 800, vinfo.yres = 480, vinfo.bits_per_pixel = 32

    char *p = (char *)mmap(NULL, lcd_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd, 0);
    if (p == MAP_FAILED) 
	{
        perror("Failed to mmap LCD memory");
        close(fd);
        close(lcd);
        return;
    }

    // 4. 读取图片的 RGB 数据
    size_t bmp_size = bmpInfoHeader.imageWidth * bmpInfoHeader.imageHeight * bmpInfoHeader.bitDepth / 8;
    char *rgb_buffer = (char *)calloc(1, bmp_size);
    if (rgb_buffer == NULL)
    {
        perror("Failed to allocate memory for RGB buffer");
        munmap(p, lcd_size);
        close(fd);
        close(lcd);
        return;
    }
    if (read(fd, rgb_buffer, bmp_size) != (ssize_t)bmp_size)
    {
        perror("Failed to read BMP RGB data");
        free(rgb_buffer);
        munmap(p, lcd_size);
        close(fd);
        close(lcd);
        return;
    }

    // 5. 将 RGB 数据复制到 LCD 显存
    int copy_width = (vinfo.xres < bmpInfoHeader.imageWidth) ? vinfo.xres : bmpInfoHeader.imageWidth;
    int copy_height = (vinfo.yres < bmpInfoHeader.imageHeight) ? vinfo.yres : bmpInfoHeader.imageHeight;
    int lcd_stride = vinfo.xres * vinfo.bits_per_pixel / 8;
    int bmp_stride = bmpInfoHeader.imageWidth * bmpInfoHeader.bitDepth / 8;

    for (int j = 0; j < copy_height; j++)
    {
        for (int i = 0; i < copy_width; i++)
        {
            memcpy(p + i * 4 + lcd_stride * j, rgb_buffer + i * 3 + bmp_stride * j, 3);
        }
    }

    // 6. 释放资源
    munmap(p, vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
    if (rgb_buffer != NULL)
    {
        free(rgb_buffer);
    }
    close(fd);
    close(lcd);
}
// 计算每行需要填充的字节数
/*
功能：这个函数的作用是计算 BMP 图像每行数据所需的填充字节数。在 BMP 图像格式中，规定每行的字节数必须是 4 的倍数。
如果一行像素数据的实际字节数不是 4 的倍数，就需要在该行数据的末尾添加若干个填充字节，使其总字节数满足 4 的倍数要求。
*/
int calculatePadding(int width, int bitDepth) 
{
    /*
    width * bitDepth 计算出一行像素的总位数。
    (width * bitDepth + 31) / 32 * 4 先将总位数加上 31（这是为了确保向上取整到最接近的 32 的倍数），然后除以 32 得到以 32 位（4 字节）为单位的数量，再乘以 4 得到按 4 字节对齐后的总字节数 rowSize。
    width * bitDepth / 8 计算出一行像素数据的实际字节数。
    最后用 rowSize - (width * bitDepth / 8) 得到需要填充的字节数。
    */
    int rowSize = (width * bitDepth + 31) / 32 * 4;
    return rowSize - (width * bitDepth / 8);
}

void CaptureLCDToFile(const char *filename) 
{
    // 打开 LCD 设备
    int lcd = open("/dev/fb0", O_RDONLY);
    if (lcd == -1) 
    {
        perror("Failed to open LCD device");
        return;
    }

    // 获取 LCD 屏幕参数
    struct fb_var_screeninfo vinfo;
    if (ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo) == -1) 
    {
        perror("Failed to get LCD screen information");
        close(lcd);
        return;
    }

    // 映射 LCD 内存
    size_t lcd_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    char *p = (char *)mmap(NULL, lcd_size, PROT_READ, MAP_SHARED, lcd, 0);
    if (p == MAP_FAILED) 
    {
        perror("Failed to mmap LCD memory");
        close(lcd);
        return;
    }

    // 计算每行需要填充的字节数
    int padding = calculatePadding(vinfo.xres, vinfo.bits_per_pixel);
    int rowSize = (vinfo.xres * vinfo.bits_per_pixel + 31) / 32 * 4;
    size_t imageDataSize = rowSize * vinfo.yres;

    // 构建 BMP 文件头
    BMPFileHeader fileHeader;
    fileHeader.fileType = 0x4D42; // 'BM' 的十六进制表示
    fileHeader.fileSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + imageDataSize;
    fileHeader.reserved1 = 0;
    fileHeader.reserved2 = 0;
    fileHeader.dataOffset = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
    
    // 构建 BMP 信息头
    BMPInfoHeader infoHeader;
    infoHeader.headerSize = sizeof(BMPInfoHeader);
    infoHeader.imageWidth = vinfo.xres;
    infoHeader.imageHeight = vinfo.yres;
    infoHeader.colorPlanes = 1;
    infoHeader.bitDepth = vinfo.bits_per_pixel;
    infoHeader.compressionMethod = 0;
    infoHeader.imageDataSize = imageDataSize;
    infoHeader.horizontalResolution = 0;
    infoHeader.verticalResolution = 0;
    infoHeader.colorsUsed = 0;
    infoHeader.importantColors = 0;

    // 打开要保存的 BMP 文件
    int out_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (out_fd == -1) 
    {
        perror("Failed to open output file");
        munmap(p, lcd_size);
        close(lcd);
        return;
    }

    // 写入 BMP 文件头
    if (write(out_fd, &fileHeader, sizeof(BMPFileHeader)) != sizeof(BMPFileHeader)) 
    {
        perror("Failed to write BMP file header");
        munmap(p, lcd_size);
        close(lcd);
        close(out_fd);
        return;
    }

    // 写入 BMP 信息头
    if (write(out_fd, &infoHeader, sizeof(BMPInfoHeader)) != sizeof(BMPInfoHeader)) 
    {
        perror("Failed to write BMP info header");
        munmap(p, lcd_size);
        close(lcd);
        close(out_fd);
        return;
    }

    int copy_width = vinfo.xres;
    int copy_height = vinfo.yres;
    int lcd_stride = vinfo.xres * vinfo.bits_per_pixel / 8;
    int bmp_stride = vinfo.xres * vinfo.bits_per_pixel / 8;

    if (vinfo.bits_per_pixel == 32) 
    {
        printf("vinfo.bits_per_pixel = 32 ---new 3.23 23:07\n");
        // 分配一行数据的缓冲区，包含填充字节
        unsigned char *rowBuffer = (unsigned char *)malloc(rowSize);
        if (rowBuffer == NULL) {
            perror("Failed to allocate memory for row buffer");
            munmap(p, lcd_size);
            close(lcd);
            close(out_fd);
            return;
        }

        for (int j = 0; j < copy_height; j++) 
        {
            int bufferIndex = 0;
            for (int i = 0; i < copy_width; i++) 
            {
                // 从 LCD 内存读取 4 字节像素数据
                unsigned int pixel = *((unsigned int *)(p + i * 4 + lcd_stride * j));
                // 提取 RGB 分量（假设为 ARGB 格式，这里忽略 A 通道）
                unsigned char red = (pixel >> 16) & 0xFF;
                unsigned char green = (pixel >> 8) & 0xFF;
                unsigned char blue = pixel & 0xFF;
                // 将 RGB 分量存入缓冲区
                rowBuffer[bufferIndex++] = blue;
                rowBuffer[bufferIndex++] = green;
                rowBuffer[bufferIndex++] = red;
            }
            // 写入填充字节到缓冲区
            for (int i = 0; i < padding; i++) 
            {
                rowBuffer[bufferIndex++] = '\0';
            }
            // 一次性写入一行数据
            if (write(out_fd, rowBuffer, rowSize) != rowSize) 
            {
                perror("Failed to write row data to file");
                free(rowBuffer);
                munmap(p, lcd_size);
                close(lcd);
                close(out_fd);
                return;
            }
        }
        free(rowBuffer);
    }

    // 释放资源
    munmap(p, lcd_size);
    close(lcd);
    close(out_fd);
}